// 自己实现深拷贝 (类型判断) 递归 拷贝对象 (函数一般不拷贝)
function deepClone(obj,hash = new WeakMap()){

  // null 和 undefined 在 == 号的情况下相等
  if(obj == undefined) return obj;

  // 另外以下类型也不需要进行拷贝:
  // string number boolean
  // symbol
  // function(函数是不需要拷贝的,我们一般不会在函数下挂属性)
  if(typeof obj !== 'object') return obj;
  //↑ todo map set

  // 正则 日期
  if(obj instanceof RegExp) return new RegExp(obj);
  if(obj instanceof Date) return new Date(obj);

  // [] / {}  cloneObj
  const val = hash.get(obj); // 如果映射表中存在 直接将结果返回
  if(val){ // 递归要有终止条件 这个就是终止条件
    return val
  }
  //↑ 说明是循环引用, 不再往下进行循环拷贝 直接返回

  /*
      obj.constructor -> f Object(){[native code]} | f Array(){[native code]}
      cloneObj -> {} | []
  */
  const cloneObj = new obj.constructor;
  hash.set(obj,cloneObj); // 如果不存在就创建映射 , 使用WeakMap主要是防止obj的那个变量指向null后还在map中被占用(weakmap键名所用的对象不会被计入垃圾回收机制,一旦消除对该节点的引用,它占用的内存就会被垃圾回收机制释放。Weakmap 保存的这个键值对,也会自动消失。)
  for(const key in obj){ // 无论是对象还是数组 都可以循环 注意 in 会遍历当前对象上的属性 和 __proto__指代的属性
    if(obj.hasOwnProperty(key)){ // 只要实例上的属性
      // 有可能 当前每一项中 内部还是一个引用类型
      // 做一个映射关系 把他存起来
      // 数据结构 队列 栈 链表 集合set hash表 树 图
      cloneObj[key] = deepClone(obj[key],hash);
      //todo symbol
    }
  }
  return cloneObj;
}

// let arr = [1,2,3,[4]]
// let newArr = deepClone(arr);
// newArr[3][0] = 100;
// console.log(arr);

let obj = {a:1};
obj.b = obj; // 循环引用
console.log(obj); //{ a: 1, b: [Circular] }
//js里是允许循环引用的 但如果我们deepClone里没有 防止循环引用 是会爆栈的
// console.log(deepClone(obj));
